﻿using System;
using System.Collections.Generic;

namespace Duellum.Core
{
	public interface IObjectPool<TKey, TValue>
	{
		TValue this[TKey key] { get; }
	}

	///http://en.wikipedia.org/wiki/Object_pool
	public abstract class ObjectPool<TKey, TValue> : MarshalByRefObject, IObjectPool<TKey, TValue>
	{
		protected IDictionary<TKey, TValue> InnerList;
		//protected IDictionary<TKey, Thread> BeingCreatedList = new Dictionary<TKey,Thread>();

		//[Obsolete("Not in use, but could be useful")]
		//static public bool AreArraysEqual(Array a, Array b)
		//{
		//    if (a == b) return true;
		//    if (a == null ||  b == null) return false;
		//    if (a.Length != b.Length) return false;
		//    for (int i=0; i < a.Length; i++) if (((IList)a)[i] != ((IList)b)[i]) return false;
		//    return true;
		//}

		public ObjectPool()
		{
			InnerList = new Dictionary<TKey,TValue>();
		}
		
		public ObjectPool(IComparer<TKey> comparer)
		{
			InnerList = new SortedList<TKey,TValue>(comparer);
		}

		public ObjectPool(IEqualityComparer<TKey> comparer)
		{
			InnerList = new Dictionary<TKey,TValue>(comparer);
		}
		
		protected abstract TValue CreateItem(TKey key);

		public TValue this[TKey key]
		{
			get { return GetOrCreateItem(key); }
		}

		public TValue Get(TKey key)
		{
			return GetOrCreateItem(key);
		}
		
		public int Count
		{
			get { return InnerList.Count; }
		}

		TValue GetOrCreateItem(TKey key)
		{
			///TODO: Use ReaderWriterLock here
			TValue value;
			if (InnerList.TryGetValue(key, out value)) {
				return value;
			} else {
				//using (TimedLock.Lock(InnerList)) {
				lock (InnerList) {
					///REVIEW: This BeingCreatedList is a very ugly hack
					/// I should be able to find a better synchronization mechanism now
					//Avoid object creation loops
					//Thread thread;
					//while (BeingCreatedList.TryGetValue(key, out thread)) {
					//    if (thread == Thread.CurrentThread) {
					//        throw new ApplicationException(string.Format("Creation loop for pooled object identified by \"{0}\"", key.ToString()));
					//    }
					//    //TOREVIEW: There's no way to another thread to get here. This is inside a lock.
					//    Thread.Sleep(0);
					//}
					if (InnerList.TryGetValue(key, out value)) {
						return value;
					} else {
						
						//BeingCreatedList.Add(key, Thread.CurrentThread);
						value = CreateItem(key);
						//BeingCreatedList.Remove(key);
						
						InnerList.Add(key, value);
						return value;
					}
				}
			}
		}
	}
}
